--- /dev/null
+/*
+
+ Support for Destinator POI's, Itineraries and Tracklogs.
+ ( as described at "http://mozoft.com/d3log.html" )
+
+ Copyright (C) 2008 Olaf Klein, o.b.klein@gpsbabel.org
+
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+
+ */
+
+#include "defs.h"
+#include "cet.h"
+#include "garmin_fs.h"
+#include "strptime.h"
+#include <ctype.h>
+#include <time.h>
+
+#define MYNAME "destinator"
+#define DST_DYN_POI "Dynamic POI"
+#define DST_ITINERARY "City->Street"
+
+static
+arglist_t destinator_args[] = {
+ ARG_TERMINATOR
+};
+
+static gbfile *fin, *fout;
+static gpsdata_type data_type;
+
+
+/*******************************************************************************/
+/* READER */
+/*-----------------------------------------------------------------------------*/
+
+static garmin_fs_t *
+gmsd_init(waypoint *wpt)
+{
+ garmin_fs_t *gmsd = GMSD_FIND(wpt);
+ if (gmsd == NULL) {
+ gmsd = garmin_fs_alloc(-1);
+ fs_chain_add(&wpt->fs, (format_specific_data *) gmsd);
+ }
+ return gmsd;
+}
+
+static char *
+read_wcstr(const int discard)
+{
+ short *buff = NULL, c;
+ int size = 0, pos = 0;
+
+ while ((c = gbfgetint16(fin))) {
+ if (size == 0) {
+ size = 16;
+ buff = xmalloc(size * 2);
+ }
+ else if (pos == size) {
+ size += 16;
+ buff = xrealloc(buff, size * 2);
+ }
+ buff[pos] = c;
+ pos += 1;
+ }
+
+ if (pos != 0) {
+ char *res;
+ if (discard) res = NULL;
+ else {
+ res = cet_str_uni_to_utf8(buff, pos);
+ res = lrtrim(res);
+ if (*res == '\0') {
+ xfree(res);
+ res = NULL;
+ }
+ }
+ xfree(buff);
+ return res;
+ }
+ else
+ return NULL;
+}
+
+static void
+write_wcstr(const char *str)
+{
+ if (str && *str) {
+ int bytes, value;
+ char *cin = (char *)str;
+ char *ce = cin + strlen(cin);
+ while (cin < ce) {
+ cet_utf8_to_ucs4(cin, &bytes, &value);
+ cin += bytes;
+ gbfputint16(value, fout);
+ }
+ }
+ gbfputint16(0, fout);
+}
+
+static int
+read_until_wcstr(const char *str)
+{
+ char *buff;
+ int len, sz;
+ int eos = 0, res = 0;
+
+ len = strlen(str);
+ sz = (len + 1) * 2;
+ buff = xcalloc(sz, 1);
+
+ while (! gbfeof(fin)) {
+
+ char c = gbfgetc(fin);
+ memmove(buff, buff + 1, sz - 1);
+ buff[sz - 1] = c;
+
+ if (c == 0) {
+ eos++;
+ if (eos >= 2) { /* two or more zero bytes => end of string */
+ char *test = cet_str_uni_to_utf8((short *)buff, len);
+ if (test) {
+ res = (strcmp(str, test) == 0);
+ xfree(test);
+ if (res) break;
+ }
+ }
+ }
+ else eos = 0;
+ }
+ xfree(buff);
+ return res;
+}
+
+static void
+destinator_read_poi(void)
+{
+ waypoint *wpt;
+ int count = 0;
+
+ gbfrewind(fin);
+
+ while (! (gbfeof(fin))) {
+ char *str, *hnum;
+ double ll;
+ garmin_fs_t *gmsd;
+
+ if (count == 0) {
+ str = read_wcstr(0);
+ if ((str == NULL) || (strcmp(str, DST_DYN_POI) != 0))
+ fatal(MYNAME "_poi: Invalid record header!\n");
+ xfree(str);
+ }
+ else if (! read_until_wcstr(DST_DYN_POI)) break;
+
+ count++;
+
+ wpt = waypt_new();
+
+ wpt->shortname = read_wcstr(0);
+ wpt->notes = read_wcstr(0); /* comment */
+
+ hnum = read_wcstr(0); /* house number */
+
+ str = read_wcstr(0); /* street */
+ if (!str) {
+ str = hnum;
+ hnum = NULL;
+ }
+ if (str) {
+ gmsd = gmsd_init(wpt);
+ if (hnum) {
+ str = xstrappend(str, " ");
+ str = xstrappend(str, hnum);
+ }
+ GMSD_SET(addr, str);
+ }
+
+ if ((str = read_wcstr(0))) { /* city */
+ gmsd = gmsd_init(wpt);
+ GMSD_SET(city, str);
+ }
+
+ if (hnum) xfree(hnum);
+
+ (void) read_wcstr(1); /* unknown */
+
+ if ((str = read_wcstr(0))) { /* postcode */
+ gmsd = gmsd_init(wpt);
+ GMSD_SET(postal_code, str);
+ }
+
+ (void) read_wcstr(1); /* unknown */
+
+ (void) gbfgetdbl(fin);
+
+ wpt->longitude = gbfgetdbl(fin);
+ wpt->latitude = gbfgetdbl(fin);
+ ll = gbfgetdbl(fin);
+ if (ll != wpt->longitude)
+ fatal(MYNAME "_poi: Invalid file!\n");
+ ll = gbfgetdbl(fin);
+ if (ll != wpt->latitude)
+ fatal(MYNAME "_poi: Invalid file!\n");
+
+ waypt_add(wpt);
+ }
+}
+
+static void
+destinator_read_rte(void)
+{
+ int count = 0;
+ route_head *rte = NULL;
+
+ gbfrewind(fin);
+
+ while (! (gbfeof(fin))) {
+ char *str;
+ waypoint *wpt;
+
+ if (count == 0) {
+ str = read_wcstr(0);
+ if ((str == NULL) || (strcmp(str, DST_ITINERARY) != 0))
+ fatal(MYNAME "_itn: Invalid record header!\n");
+ xfree(str);
+ }
+ else if (! read_until_wcstr(DST_ITINERARY)) break;
+
+ count++;
+
+ wpt = waypt_new();
+
+ wpt->shortname = read_wcstr(0);
+ wpt->notes = read_wcstr(0);
+
+ (void) gbfgetint32(fin);
+ (void) gbfgetdbl(fin);
+ (void) gbfgetdbl(fin);
+
+ wpt->longitude = gbfgetdbl(fin);
+ wpt->latitude = gbfgetdbl(fin);
+ if (gbfgetdbl(fin) != wpt->longitude)
+ fatal(MYNAME "_itn: Invalid file!\n");
+ if (gbfgetdbl(fin) != wpt->latitude)
+ fatal(MYNAME "_itn: Invalid file!\n");
+
+ if (! rte) {
+ rte = route_head_alloc();
+ route_add_head(rte);
+ }
+ route_add_wpt(rte, wpt);
+
+ (void) gbfgetdbl(fin);
+ (void) gbfgetdbl(fin);
+ }
+}
+
+static void
+destinator_read_trk(void)
+{
+ char TXT[4] = "TXT";
+ int recno = -1;
+ route_head *trk = NULL;
+
+ gbfrewind(fin);
+
+ while (! (gbfeof(fin))) {
+ waypoint *wpt;
+ struct tm tm;
+ char buff[20];
+ int date;
+ double time;
+
+ recno++;
+
+ if (gbfeof(fin)) break;
+
+ wpt = waypt_new();
+
+ wpt->longitude = gbfgetdbl(fin);
+ wpt->latitude = gbfgetdbl(fin);
+ wpt->altitude = gbfgetdbl(fin);
+
+ (void) gbfgetdbl(fin); /* unknown */
+ (void) gbfgetdbl(fin); /* unknown */
+ (void) gbfgetdbl(fin); /* unknown */
+
+ wpt->fix = gbfgetint32(fin);
+ wpt->sat = gbfgetint32(fin);
+
+ gbfseek(fin, 12 * sizeof(gbint32), SEEK_CUR); /* SAT info */
+
+ date = gbfgetint32(fin);
+ time = gbfgetflt(fin);
+
+ gbfseek(fin, 2 * 12, SEEK_CUR); /* SAT info */
+
+ gbfread(TXT, 1, 3, fin);
+ if (strcmp(TXT, "TXT") != 0)
+ fatal(MYNAME "_trk: No (or unknown) file!\n");
+
+ gbfseek(fin, 13, SEEK_CUR); /* unknown */
+
+ memset(&tm, 0, sizeof(tm));
+
+ snprintf(buff, sizeof(buff), "%06d%.f", date, time);
+ strptime(buff, "%d%m%y%H%M%S", &tm);
+ wpt->creation_time = mkgmtime(&tm);
+ wpt->microseconds = ((int)time % 1000) * 1000;
+
+ if ((wpt->sat > 0) && (wpt->fix > 0)) {
+
+ wpt->fix++;
+
+ if (! trk) {
+ trk = route_head_alloc();
+ track_add_head(trk);
+ }
+
+ track_add_wpt(trk, wpt);
+ }
+ else
+ waypt_free(wpt);
+ }
+}
+
+static void
+destinator_read(void)
+{
+ int i0, i1;
+ double d0, d1;
+ char buff[16];
+
+ gbfread(buff, sizeof(buff), 1, fin);
+ i0 = le_read32(&buff[0]);
+ i1 = le_read32(&buff[4]);
+
+ if ((i0 == 0x690043) && (i1 == 0x790074)) {
+ if (data_type != rtedata)
+ warning(MYNAME ": Using Desinator Itinerary Format!\n");
+ destinator_read_rte();
+ }
+ else if ((i0 == 0x790044) && (i1 == 0x61006e)) {
+ if (data_type != wptdata)
+ warning(MYNAME ": Using Desinator POI Format!\n");
+ destinator_read_poi();
+ }
+ else {
+ if (data_type != trkdata)
+ warning(MYNAME ": Using Desinator Tracklog Format!\n");
+
+ le_read64(&d0, &buff[0]);
+ le_read64(&d1, &buff[8]);
+ if ((fabs(d0) > 180) || (fabs(d1) > 90))
+ fatal(MYNAME ": No Destinator (.dat) file!\n");
+ destinator_read_trk();
+ }
+}
+
+/*******************************************************************************/
+/* WRITER */
+/*-----------------------------------------------------------------------------*/
+
+static void
+destinator_wpt_disp(const waypoint *wpt)
+{
+ garmin_fs_t *gmsd = GMSD_FIND(wpt);
+
+ write_wcstr(DST_DYN_POI);
+ write_wcstr((wpt->shortname) ? wpt->shortname : "WPT");
+ write_wcstr((wpt->notes) ? wpt->notes : wpt->description);
+
+ write_wcstr(NULL); /* house number */
+ write_wcstr(GMSD_GET(addr, NULL)); /* street */
+ write_wcstr(GMSD_GET(city, NULL)); /* city */
+ write_wcstr(NULL); /* unknown */
+ write_wcstr(GMSD_GET(postal_code, NULL)); /* postcode */
+ write_wcstr(NULL); /* unknown */
+
+ gbfputint32(0, fout);
+ gbfputint32(0, fout);
+
+ gbfputdbl(wpt->longitude, fout);
+ gbfputdbl(wpt->latitude, fout);
+ gbfputdbl(wpt->longitude, fout);
+ gbfputdbl(wpt->latitude, fout);
+
+ gbfputdbl(0, fout);
+ gbfputdbl(0, fout);
+}
+
+static void
+destinator_trkpt_disp(const waypoint *wpt)
+{
+ int i;
+
+ gbfputdbl(wpt->longitude, fout);
+ gbfputdbl(wpt->latitude, fout);
+ gbfputdbl(wpt->altitude, fout);
+ gbfputdbl(0, fout);
+ gbfputdbl(0, fout);
+ gbfputdbl(0, fout);
+ gbfputint32(wpt->fix > fix_unknown ? wpt->fix - 1 : 0, fout);
+ gbfputint32(wpt->sat, fout);
+ for (i = 0; i < 12; i++) gbfputint32(0, fout);
+
+ if (wpt->creation_time) {
+ struct tm tm;
+ double time;
+ int date;
+
+ tm = *gmtime(&wpt->creation_time);
+ tm.tm_mon += 1;
+ tm.tm_year -= 100;
+ date = ((int)tm.tm_mday * 10000) + ((int)tm.tm_mon * 100) + tm.tm_year;
+ gbfputint32(date, fout);
+
+ time = ((int)tm.tm_hour * 10000) + ((int)tm.tm_min * 100) + tm.tm_sec;
+ time = (time * 1000) + (wpt->microseconds / 1000);
+ gbfputflt(time, fout);
+ }
+ else {
+ gbfputint32(0, fout); /* Is this invalid ? */
+ gbfputflt(0, fout);
+ }
+
+ for (i = 0; i < 12; i++) gbfputint16(0, fout);
+ gbfputcstr("TXT", fout);
+ for (i = 0; i < 12; i++) gbfputc(0, fout);
+}
+
+static void
+destinator_rtept_disp(const waypoint *wpt)
+{
+ write_wcstr(DST_ITINERARY);
+ write_wcstr((wpt->shortname) ? wpt->shortname : "RTEPT");
+ write_wcstr((wpt->notes) ? wpt->notes : wpt->description);
+
+ gbfputint32(0, fout);
+ gbfputdbl(0, fout);
+ gbfputdbl(0, fout);
+
+ gbfputdbl(wpt->longitude, fout);
+ gbfputdbl(wpt->latitude, fout);
+ gbfputdbl(wpt->longitude, fout);
+ gbfputdbl(wpt->latitude, fout);
+
+ gbfputdbl(0, fout);
+ gbfputdbl(0, fout);
+}
+
+/*******************************************************************************
+* %%% global callbacks called by gpsbabel main process %%% *
+*******************************************************************************/
+
+static void
+destinator_rd_init(const char *fname)
+{
+ fin = gbfopen_le(fname, "rb", MYNAME);
+}
+
+static void
+destinator_rd_deinit(void)
+{
+ gbfclose(fin);
+}
+
+static void
+destinator_read_poi_wrapper(void)
+{
+ data_type = wptdata;
+ destinator_read();
+}
+
+static void
+destinator_read_rte_wrapper(void)
+{
+ data_type = rtedata;
+ destinator_read();
+}
+
+static void
+destinator_read_trk_wrapper(void)
+{
+ data_type = trkdata;
+ destinator_read();
+}
+
+static void
+destinator_wr_init(const char *fname)
+{
+ fout = gbfopen_le(fname, "wb", MYNAME);
+}
+
+static void
+destinator_wr_deinit(void)
+{
+ gbfclose(fout);
+}
+
+static void
+destinator_write_poi(void)
+{
+ waypt_disp_all(destinator_wpt_disp);
+}
+
+static void
+destinator_write_rte(void)
+{
+ route_disp_all(NULL, NULL, destinator_rtept_disp);
+}
+
+static void
+destinator_write_trk(void)
+{
+ track_disp_all(NULL, NULL, destinator_trkpt_disp);
+}
+
+/**************************************************************************/
+
+ff_vecs_t destinator_poi_vecs = {
+ ff_type_file,
+ {
+ ff_cap_read | ff_cap_write /* waypoints */,
+ ff_cap_none /* tracks */,
+ ff_cap_none /* routes */
+ },
+ destinator_rd_init,
+ destinator_wr_init,
+ destinator_rd_deinit,
+ destinator_wr_deinit,
+ destinator_read_poi_wrapper,
+ destinator_write_poi,
+ NULL,
+ destinator_args,
+ CET_CHARSET_UTF8, 1 /* fixed */
+};
+
+ff_vecs_t destinator_rte_vecs = {
+ ff_type_file,
+ {
+ ff_cap_none /* waypoints */,
+ ff_cap_none /* tracks */,
+ ff_cap_read | ff_cap_write /* routes */
+ },
+ destinator_rd_init,
+ destinator_wr_init,
+ destinator_rd_deinit,
+ destinator_wr_deinit,
+ destinator_read_rte_wrapper,
+ destinator_write_rte,
+ NULL,
+ destinator_args,
+ CET_CHARSET_UTF8, 1 /* fixed */
+};
+
+ff_vecs_t destinator_trk_vecs = {
+ ff_type_file,
+ {
+ ff_cap_none /* waypoints */,
+ ff_cap_read | ff_cap_write /* tracks */,
+ ff_cap_none /* routes */
+ },
+ destinator_rd_init,
+ destinator_wr_init,
+ destinator_rd_deinit,
+ destinator_wr_deinit,
+ destinator_read_trk_wrapper,
+ destinator_write_trk,
+ NULL,
+ destinator_args,
+ CET_CHARSET_UTF8, 1 /* fixed */
+};
+
+/**************************************************************************/